package cn.cas.mango.service;

import cn.cas.mango.dto.HotListDetail;
import cn.cas.mango.dto.Message;
import cn.cas.mango.dto.param.SearchHotParam;
import cn.cas.mango.service.samenews.SameNewsService;
import cn.cas.mango.util.RequestUtil.HotListDetailRequest;
import cn.cas.mango.util.RequestUtil.SearchHotRequest;
import cn.cas.mango.util.ResponseUtil.ResponseUtil;
import cn.cas.mango.util.TimeUtil;
import cn.cas.mango.util.paramCheck.HotListDetailParamCheckUtil;
import cn.cas.mango.util.paramCheck.SearchHotParamCheckUtil;
import cn.cas.mango.util.singlepass.SinglePass;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.Hit;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@Slf4j
public class SearchHotService {
  @Autowired
  private HotListDetailParamCheckUtil checkUtil;
  @Autowired
  private ConfigService config;
  @Autowired
  ElasticsearchClient elasticsearchClient;
  @Autowired
  private SearchHotParamCheckUtil searchHotParamCheckUtil;
  @Autowired
  private SearchHotRequest searchHotRequest;

  // 包装item成json形式
  private JSONObject wrapCores(HotListDetail core) {
    String title = core.getTitle();
    String key = core.getKey();
    Object platform = core.getPlatform();
    String platformName = core.getPlatformName();
    Integer maxHotValue = core.getMaxHotValue();
    Integer duration = core.getDuration();
    Integer maxRankTop = core.getMaxRankTop();
    Object subject = core.getSubject();
    String  insertTime = core.getInsert_time();
    JSONArray geo = new JSONArray();
    JSONArray domainLabel = new JSONArray();
    JSONObject coreObject = new JSONObject();
    coreObject.put("title", title);
    coreObject.put("key", key);
    coreObject.put("platformCode", platform);
    coreObject.put("platformName", platformName);
    coreObject.put("maxHotValue", ResponseUtil.wrapMaxHotValueFormat(maxHotValue == null ? 1 : maxHotValue));
    coreObject.put("duration", ResponseUtil.wrapDuration(duration == null ? 1 : duration));
    coreObject.put("maxRankTop", maxRankTop == null ? 1 : maxRankTop);
    List<String> strings = new ArrayList<>(ResponseUtil.wrappedKeyword(subject));
    coreObject.put("subject", strings.isEmpty() ? "": strings.get(0));
//    coreObject.put("subject", subject == null? "": subject.toString());
    coreObject.put("firstInsertTime", insertTime);
    coreObject.put("sim", 1.0);
    coreObject.put("domainLabel", domainLabel);
    return coreObject;
  }

  // 包装array成json形式
  private JSONArray wrapDataArray(List<HotListDetail> list) {
    JSONArray ret = new JSONArray();
    for (HotListDetail hotListDetail: list) {
      ret.add(wrapCores(hotListDetail));
    }
    return ret;
  }

  // 搜索的接口
  public Message searchHot(SearchHotParam param) {
    searchHotParamCheckUtil.check(param);
    log.info("【uuid: {}】, query param: {}", param.getUuid(), param);
    SearchRequest.Builder builder = searchHotRequest.buildRequest(param);
    SearchResponse<HotListDetail> response;
    SearchRequest build = builder.build();
    log.info("【uuid: {}】, searchHot request {}", param.getUuid(), build);
    try {
     response = elasticsearchClient.search(build, HotListDetail.class);
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
    int total = (int)response.hits().total().value();
    int totalPage = (int) (Math.ceil((double) total / param.getPageSize()));
    List<Hit<HotListDetail>> hits = response.hits().hits();
    JSONArray dataArray = wrapDataArray(hits.stream().map(x->x.source()).collect(Collectors.toList()));
    JSONObject data = new JSONObject();
    JSONObject result = new JSONObject();
    result.put("total", total);
    result.put("pageNo", param.getPageNo());
    result.put("totalPage", totalPage);
    result.put("aggMap", null);
    result.put("data", dataArray);
    data.put("result", result);
    if (param.getIfAgg()) {
      SameNewsService sameNewsService = new SameNewsService(hits, SortOrder.Desc, "_score");
      TimeUtil timeTotal = new TimeUtil();
      Map<String, List<Hit<HotListDetail>>> sameNewsList = sameNewsService.getSameNewsList(param.getLikeDegree());

      log.info("【uuid: {}】, keywords cluster total cost: {}s ", param.getUuid(), timeTotal.getCost());
      Map<String, Hit<HotListDetail>> newsMap = wrapNewsMap(hits);

      List<List<Hit<HotListDetail>>> returnNews = getReturnNews(newsMap, sameNewsList, 0, Integer.MAX_VALUE);
      List<List<HotListDetail>> lists = deWrapNews(returnNews);

      double aggWeight = config.getSearchHotAggregationWeight() * config.getAggTimes();
      SinglePass singlePass = new SinglePass(lists, aggWeight, config.getVectorSize());
      log.info("【uuid: {}】, searchHot aggWeight: {}", param.getUuid(), aggWeight);
      List<List<HotListDetail>> clusters = singlePass.getClusters();
      JSONObject aggResult = new JSONObject();
      aggResult.put("total", clusters.size());
      JSONArray aggArray = new JSONArray();
      for (List<HotListDetail> list: clusters) {
        HotListDetail core = list.get(0);
        JSONObject coreObject = wrapCores(core);
        coreObject.put("simHotNum", list.size() - 1);
        if (list.size() == 1) {
          coreObject.put("agg", new JSONArray());
        } else {
          List<HotListDetail> left = list.subList(1, list.size());
          JSONArray jsonArray = new JSONArray();
          for (HotListDetail hotListDetail: left) {
            JSONObject leftObject = wrapCores(hotListDetail);
            leftObject.put("sim", 1);
            leftObject.put("geoLabel", null);
            jsonArray.add(leftObject);
          }
          coreObject.put("agg", jsonArray);
        }
        aggArray.add(coreObject);
      }
      aggResult.put("data", aggArray);
      data.put("aggResult", aggResult);
    }

    return Message.builder().code(200).data(data).msg("200 OK").build();
  }

  private List<List<HotListDetail>> deWrapNews(List<List<Hit<HotListDetail>>> news) {
    List<List<HotListDetail>> ret = new ArrayList<>();
    for (List<Hit<HotListDetail>> hits : news) {
      List<HotListDetail> collect = hits.stream().map(x -> x.source()).collect(Collectors.toList());
      ret.add(collect);
    }
    return ret;
  }

  private Map<String, Hit<HotListDetail>> wrapNewsMap(List<Hit<HotListDetail>> newsList) {
    Map<String, Hit<HotListDetail>> ret = new HashMap<>();
    for (Hit<HotListDetail> hit: newsList) {
      HotListDetail source = hit.source();
      String key = source.getKey();
      ret.put(key, hit);
    }
    return ret;
  }

  // 返回新闻
  private List<List<Hit<HotListDetail>>> getReturnNews(Map<String, Hit<HotListDetail>> newsMap,
                                                       Map<String, List<Hit<HotListDetail>>> sameNewsList,
                                                       int from, int size) {
    List<List<Hit<HotListDetail>>> ret = new ArrayList<>();
    List<String> coreKeyList = new ArrayList<>(sameNewsList.keySet());
    if (from > newsMap.size()) {
      return new ArrayList<>();
    }
    List<String> cutoffList = coreKeyList.subList(from, Math.min(from + size, coreKeyList.size()));
    for (String key: cutoffList) {
      List<Hit<HotListDetail>> temp = new ArrayList<>();
      Hit<HotListDetail> core = newsMap.get(key);
      temp.add(core);
      temp.addAll(sameNewsList.get(key));
      ret.add(temp);
    }
    return ret;
  }
}
